﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Smile.Serialization
{
    /// <summary>
    ///     This class can serialize or deserialize a XML object. A serializable/deserializable
    ///     object can have empty properties. You must declare the
    ///     [XmlElementAttribute(IsNullable = true)] before their declaration to specify that
    ///     the property can be empty.
    /// </summary>
    ///
    /// <typeparam name="T">Generic type parameter.</typeparam>
    public class XmlSerializer<T> where T : class, new()
    {
        /// <summary>Serializes the given object to a XML formated string.</summary>
        ///
        /// <param name="objectToSerialize">The object to serialize.</param>
        ///
        /// <returns>The XML formated string.</returns>
        public static string Serialize(T objectToSerialize)
        {
            var serializer = new XmlSerializer(objectToSerialize.GetType());
            var ms = new MemoryStream();
            serializer.Serialize(ms, objectToSerialize);
            byte[] msArray = ms.ToArray();
            string xml = Encoding.UTF8.GetString(msArray, 0, msArray.Length);

            return xml;
        }

        /// <summary>
        ///     (Asynchronous) Serializes the given object to a XML formated string.
        /// </summary>
        ///
        /// <param name="objectToSerialize">The object to serialize.</param>
        ///
        /// <returns>The XML formated string.</returns>
        public static async Task<string> SerializeAsync(T objectToSerialize)
        {
            var result = await Task<string>.Run(() => Serialize(objectToSerialize));
            return result;
        }

        /// <summary>Deserializes a XML formated string to an object.</summary>
        ///
        /// <param name="stringToDeserialize">The string to deserialize.</param>
        ///
        /// <returns>The deserialized object.</returns>
        public static T Deserialize(string stringToDeserialize)
        {
            var objectToDeserialize = new T();

            using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(stringToDeserialize)))
            {
                var serializer = new XmlSerializer(objectToDeserialize.GetType());
                
                objectToDeserialize = (T)serializer.Deserialize(ms);
            }

            return objectToDeserialize;
        }

        /// <summary>(Asynchronous) Deserializes a XML formated string to an object.</summary>
        ///
        /// <param name="stringToDeserialize">The string to deserialize.</param>
        ///
        /// <returns>The deserialized object.</returns>
        public static async Task<T> DeserializeAsync(string stringToDeserialize)
        {
            var result = await Task<T>.Run(() => Deserialize(stringToDeserialize));
            return result;
        }
    }
}
